package com.efast.dmom.mgmt.service.les.impl;

import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.baomidou.mybatisplus.service.impl.ServiceImpl;
import com.efast.cafe.framework.exception.ServiceException;
import com.efast.cafe.framework.jdbc.JdbcDao;
import com.efast.cafe.framework.util.SpringWebUtils;
import com.efast.dmom.mgmt.bean.les.LocDlocWhBean;
import com.efast.dmom.mgmt.dao.les.*;
import com.efast.dmom.mgmt.dao.master.TmBasLocDao;
import com.efast.dmom.mgmt.dao.wms.TwBasPartRuleDao;
import com.efast.dmom.mgmt.dao.wms.TwBasPartRuleHeaderDao;
import com.efast.dmom.mgmt.entity.les.*;
import com.efast.dmom.mgmt.entity.master.TmBasLoc;
import com.efast.dmom.mgmt.entity.wms.TwBasPartRule;
import com.efast.dmom.mgmt.entity.wms.TwBasPartRuleHeader;
import com.efast.dmom.mgmt.service.les.TwMovementHeaderService;
import com.efast.dmom.mgmt.utils.Assist;

/**
 * <p>
 * 库存移动单（表头） 服务实现类
 * </p>
 *
 * @author ChenLiPeng
 * @since 2018-05-16
 */
@Service
public class TwMovementHeaderServiceImpl extends ServiceImpl<TwMovementHeaderDao, TwMovementHeader>
        implements TwMovementHeaderService {

    /**
     * 库存移动单（表头）
     */
    private final TwMovementHeaderDao twMovementHeaderDao;
    /**
     * 库存移动单（明细）
     */
    private final TwMovementDetailsDao twMovementDetailsDao;
    /**
     * 移库分配表-目标地点
     */
    private final TwActStockAllocDMapper twActStockAllocDMapper;
    /**
     * 移动分配表-源地点
     */
    private final TwActStockAllocSMapper twActStockAllocSMapper;
    /**
     * 零件仓储关系
     */
    private final TrBasPartStorageDao trBasPartStorageDao;
    /**
     * 库存表
     */
    private final TwInvSkuLoctlDao twInvSkuLoctlDao;
    /**
     * 零件规则组头配置 tw_bas_part_rule
     */
    private final TwBasPartRuleDao twBasPartRuleDao;
    /**
     * 零件规则 tw_bas_part_rule_header
     */
    private final TwBasPartRuleHeaderDao twBasPartRuleHeaderDao;
    /**
     * 主数据库位 tm_bas_loc
     */
    private final TmBasLocDao tmBasLocDao;

    @Qualifier("jdbcDao")
    @Autowired
    private JdbcDao jdbcDao;

    @Autowired
    public TwMovementHeaderServiceImpl(TwMovementHeaderDao twMovementHeaderDao,
                                       TwMovementDetailsDao twMovementDetailsDao, TwActStockAllocDMapper twActStockAllocDMapper,
                                       TwActStockAllocSMapper twActStockAllocSMapper, TrBasPartStorageDao trBasPartStorageDao,
                                       TwInvSkuLoctlDao twInvSkuLoctlDao, TwBasPartRuleDao twBasPartRuleDao,
                                       TwBasPartRuleHeaderDao twBasPartRuleHeaderDao, TmBasLocDao tmBasLocDao) {
        this.twMovementHeaderDao = twMovementHeaderDao;
        this.twMovementDetailsDao = twMovementDetailsDao;
        this.twActStockAllocDMapper = twActStockAllocDMapper;
        this.twActStockAllocSMapper = twActStockAllocSMapper;
        this.trBasPartStorageDao = trBasPartStorageDao;
        this.twInvSkuLoctlDao = twInvSkuLoctlDao;
        this.twBasPartRuleDao = twBasPartRuleDao;
        this.twBasPartRuleHeaderDao = twBasPartRuleHeaderDao;
        this.tmBasLocDao = tmBasLocDao;
    }

    /**
     * 新增移库单头表
     *
     * @param twMovementHeader
     *            移动头表
     */
    @Override
    public void save(TwMovementHeader twMovementHeader) {
        String code = jdbcDao.executeProcGetCode("tw_movement_header", "efast");
        twMovementHeader.setSheetNo(code);
        twMovementHeader.setCreateTime(new Date());
        twMovementHeader.setCreateUser(SpringWebUtils.getRequestUser());
        twMovementHeader.setCompanyCode(SpringWebUtils.getRequestCompany());
        twMovementHeader.setMarkStatus(1);
        twMovementHeaderDao.insert(twMovementHeader);
    }

    /**
     * 通过仓库查询库区
     *
     * @param wareHouseNo
     *            仓库编号
     * @return List<LocDlocWhBean>
     */
    @Override
    public List<LocDlocWhBean> queryDlocByWh(String wareHouseNo) {
        return twMovementHeaderDao.queryDlocByWh(wareHouseNo);
    }

    /**
     * 通过库区查询库位
     *
     * @param dlocNo
     *            库区编号
     * @return List<LocDlocWhBean>
     */
    @Override
    public List<LocDlocWhBean> queryLocByDloc(String dlocNo) {
        return twMovementHeaderDao.queryLocByDloc(dlocNo);
    }

    /**
     * 关闭移库单，更新header和detail的状态
     *
     * @param twMovementHeader
     *            头表信息
     */
    @Override
    public void close(TwMovementHeader twMovementHeader) {

        // 更新header的状态
        // 移动单状态0新单1发布2完成3关闭4分配完成5部分分配
        twMovementHeader.setSheetStatus("3");
        twMovementHeaderDao.updateById(twMovementHeader);

        // 更新detail的状态
        TwMovementDetails twMovementDetails = new TwMovementDetails();
        twMovementDetails.setTwMovementHeaderId(twMovementHeader.getTwMovementHeaderId());
        twMovementDetails.setLineStatus("3");
        EntityWrapper<TwMovementDetails> ew = new EntityWrapper<>();
        ew.where("tw_movement_header_id={0}", twMovementHeader.getTwMovementHeaderId());
        twMovementDetailsDao.update(twMovementDetails, ew);
    }

    /**
     * 没有指定源库位和目标库位的，给他们分配库位 分配完成后，源分配表和目标分配表插入数据 更新库存(源库位的可用库存、待移出数量)
     *
     * @param twMovementHeader
     *            头表信息
     */
    @Override
    public void distribution(TwMovementHeader twMovementHeader) {

        twMovementHeader = twMovementHeaderDao.selectById(twMovementHeader);

        // 【1】：根据移库单的头ID，查询出对应的detail
        Map<String, Object> stringObjectMap = new HashMap<>(40);
        stringObjectMap.put("tw_movement_header_id", twMovementHeader.getTwMovementHeaderId());
        List<TwMovementDetails> listTwMovementDetails = twMovementDetailsDao.selectByMap(stringObjectMap);

        // 【2】：遍历detail，判断是否需要分配库位的
        for (TwMovementDetails twMovementDetails : listTwMovementDetails) {
            // 此时存在4中情况，
            // 【3】：根据零件号 去 零件仓库关系（tr_bas_part_storage）找一个库位
            List<TrBasPartStorage> listTrBasPartStorage = trBasPartStorageDao.selectTrBasPartStorage(new Assist(
                    Assist.andEq("tr_bas_part_storage.part_no_version", twMovementDetails.getPartNoVersion())));
            if (listTrBasPartStorage.size() == 0) {
                throw new ServiceException(twMovementDetails.getPartNoVersion() + "没有对应的零件仓储关系");
            }
            // 【4】：无源库位和无目标库位:需要分配库位
            if (twMovementDetails.getFmLocation() == null && twMovementDetails.getToLocation() == null) {
                for (TrBasPartStorage trBasPartStorage : listTrBasPartStorage) {
                    // 【4.1】 设置源库区和源库位,并且插入(tw_act_stock_alloc_s)移动分配表源地点记录
                    setFmLocation(twMovementDetails, trBasPartStorage);
                    // 【4.2】 设置目标库区和目标库位，并且插入(tw_act_stock_alloc_d)移动分配表目标地点记录
                    setToLocation(twMovementDetails, trBasPartStorage);
                    // 【4.3】 更新对应移库单detail的记录
                    twMovementDetailsDao.updateById(twMovementDetails);
                }
            }
            // 【5】：无源库位和有目标库位:需要分配库位
            if (twMovementDetails.getFmLocation() == null && twMovementDetails.getToLocation() != null) {
                for (TrBasPartStorage trBasPartStorage : listTrBasPartStorage) {
                    // 【5.1】 设置源库区和源库位,并且插入(tw_act_stock_alloc_s)移动分配表源地点记录
                    setFmLocation(twMovementDetails, trBasPartStorage);
                    // 【5.2】 更新对应移库单detail的记录
                    twMovementDetailsDao.updateById(twMovementDetails);
                }
            }
            // 【6】.有源库位和无目标库位:需要分配库位
            if (twMovementDetails.getFmLocation() != null && twMovementDetails.getToLocation() == null) {
                for (TrBasPartStorage trBasPartStorage : listTrBasPartStorage) {
                    // 【6.1】 设置目标库区和目标库位 ,并且插入(tw_act_stock_alloc_d)移动分配表目标地点 记录
                    setToLocation(twMovementDetails, trBasPartStorage);
                    // 【6.2】 更新对应移库单detail的记录
                    twMovementDetailsDao.updateById(twMovementDetails);
                }
            }
            // 【7】.有源库位和有目标库位:不需要分配库位
            if (twMovementDetails.getFmLocation() != null && twMovementDetails.getToLocation() != null) {
                System.out.println("指定了源库位和目标库位，不用分配,但需要插入分配记录.......");
                // 【7.1】库位虽然填了，不用分配库位而已，但是选哪些库存去移动，要么用户指定，比如源跟踪号，批次号等条件，要么不指定，系统根据先进先出风条件去挑选
                if (twMovementDetails.getFmLotNo() == null && twMovementDetails.getFmPartPackSerial() == null
                        && twMovementDetails.getFmSerialNo() == null) {

                    // 【7.2】：出库仍需要满足，库位限制条件1、2。入库也不需要满足规则中的各个条件，直接按照填写库位的出入库即可
                    // 根据零件号和供应商以及出库三个条件查询零件出库规则头表的数据
                    List<TwBasPartRuleHeader> twBasPartRuleHeaders = twBasPartRuleHeaderDao.selectTwBasPartRuleHeader(
                            new Assist(Assist.andEq("part_no_version", twMovementDetails.getPartNoVersion()),
                                    Assist.andEq("suppl_no", twMovementDetails.getSupplNo()),
                                    Assist.andEq("rule_type", 1)));
                    if (twBasPartRuleHeaders.size() == 0) {
                        throw new ServiceException(twMovementDetails.getPartNoVersion() + "没有配置出库规则组");
                    }
                    TwBasPartRuleHeader twBasPartRuleHeader = twBasPartRuleHeaders.get(0);
                    // 【7.3】 查询出库规则具体的信息
                    Assist assist = new Assist();
                    assist.setOrder(Assist.order("rule_order", true));
                    assist.setRequires(
                            Assist.andEq("tw_bas_part_rule_header_id", twBasPartRuleHeader.getTwBasPartRuleHeaderId()));
                    assist.setRequires(Assist.andEq("storage_rule_type", 1));
                    // tw_bas_part_rule.sheet_type = tw_movement_header.sheet_type
                    assist.setRequires(Assist.andEq("sheet_type", twMovementHeader.getSheetType()));
                    List<TwBasPartRule> listTwBasPartRule = twBasPartRuleDao.selectTwBasPartRule(assist);
                    if (listTwBasPartRule.size() == 0) {
                        throw new ServiceException(twMovementDetails.getPartNoVersion() + "零件没有出库规则");
                    }
                    TwBasPartRule twBasPartRule = listTwBasPartRule.get(0);

                    // 【7.4】 根据零件号和源库位 查询库存记录
                    Assist assist1 = new Assist();
                    if ("000".equals(twBasPartRule.getLocLimit1())) {
                        assist1.setOrder(Assist.order("lot_no", true));
                    }
                    if ("001".equals(twBasPartRule.getLocLimit1())) {
                        assist1.setOrder(Assist.order("lot_no", false));
                    }
                    assist1.setRequires(Assist.andEq("loc_no", twMovementDetails.getFmLocation()));
                    assist1.setRequires(Assist.andEq("part_no_version", twMovementDetails.getPartNoVersion()));
                    List<TwInvSkuLoctl> twInvSkuLoctls = twInvSkuLoctlDao.selectTwInvSkuLoctl(assist1);
                    if (twInvSkuLoctls.size() == 0) {
                        throw new ServiceException("零件:" + twMovementDetails.getPartNoVersion() + "库位:"
                                + twMovementDetails.getFmLocation() + "没有库存记录");
                    }
                    // 根据库位限制条件1和2，在库存表中找出一条满足条件的库存记录进行出库。
                    // 库位限制条件１：000 先进先出，001先进后出
                    // 库位限制条件２：101 匹配数量输出，102整箱不允许超额，103 整箱允许超额
                    // 举例：
                    // 库存是1箱8个，有4箱，总计是32个，是同一托盘上的
                    // 实际需要出库数量20个
                    // 101：分配20个-拆箱
                    // 102:分配16个
                    // 103:分配24个
                    // 104:分配32个：
                    // 【7.5】 ： 针对库位限制条件1和2组合出6种情况。分别对其进行出库，插入源分配记录。
                    if ("000".equals(twBasPartRule.getLocLimit1()) && "101".equals(twBasPartRule.getLocLimit2())) {
                        int needOutCount = twMovementDetails.getToQty();
                        // 先进先出 匹配数量
                        needOutCount = matchingAndFIFO(twMovementDetails, twInvSkuLoctls, needOutCount);
                        if (needOutCount > 0) {
                            throw new ServiceException("库存不足，分配失败");
                        }
                    }
                    if ("000".equals(twBasPartRule.getLocLimit1()) && "102".equals(twBasPartRule.getLocLimit2())) {
                        int needOutCount = twMovementDetails.getToQty();
                        needOutCount = notExcessFIFO(twMovementDetails, twInvSkuLoctls, needOutCount);
                        if (needOutCount > 0) {
                            throw new ServiceException("库存不足，分配失败");
                        }
                    }
                    if ("000".equals(twBasPartRule.getLocLimit1()) && "103".equals(twBasPartRule.getLocLimit2())) {
                        int needOutCount = twMovementDetails.getToQty();
                        needOutCount = excessFIFO(twMovementDetails, twInvSkuLoctls, needOutCount);
                        if (needOutCount > 0) {
                            throw new ServiceException("库存不足，分配失败");
                        }
                    }
                    if ("001".equals(twBasPartRule.getLocLimit1()) && "101".equals(twBasPartRule.getLocLimit2())) {
                        int needOutCount = twMovementDetails.getToQty();
                        needOutCount = matchingAndFIFO(twMovementDetails, twInvSkuLoctls, needOutCount);
                        if (needOutCount > 0) {
                            throw new ServiceException("库存不足，分配失败");
                        }
                    }
                    if ("001".equals(twBasPartRule.getLocLimit1()) && "102".equals(twBasPartRule.getLocLimit2())) {
                        int needOutCount = twMovementDetails.getToQty();
                        needOutCount = notExcessFIFO(twMovementDetails, twInvSkuLoctls, needOutCount);
                        if (needOutCount > 0) {
                            throw new ServiceException("库存不足，分配失败");
                        }
                    }
                    if ("001".equals(twBasPartRule.getLocLimit1()) && "103".equals(twBasPartRule.getLocLimit2())) {
                        int needOutCount = twMovementDetails.getToQty();
                        needOutCount = excessFIFO(twMovementDetails, twInvSkuLoctls, needOutCount);
                        if (needOutCount > 0) {
                            throw new ServiceException("库存不足，分配失败");
                        }
                    }
                } else {
                    // 【7.6】：说明用户已经指定了从那条记录中去移库操作，但是也分为几种情况。
                    // 如果填写了零件包装流水号，那么只能找到一条记录。
                    if (twMovementDetails.getToPartPackSerial() != null) {
                        Assist assist = new Assist();
                        assist.setRequires(Assist.andEq("part_no_version", twMovementDetails.getPartNoVersion()));
                        assist.setRequires(Assist.andEq("loc_no", twMovementDetails.getToLocation()));
                        assist.setRequires(Assist.andEq("part_pack_serial", twMovementDetails.getFmPartPackSerial()));
                        List<TwInvSkuLoctl> twInvSkuLoctls = twInvSkuLoctlDao.selectTwInvSkuLoctl(assist);
                        if (twMovementDetails.getToQty() > twInvSkuLoctls.get(0).getCurQty()) {
                            throw new ServiceException("库存不足,分配失败");
                        }
                        addTwActStockAllocSAndUpdateSku(twMovementDetails, twInvSkuLoctls.get(0),
                                twMovementDetails.getToQty());
                    } else {
                        // 说明填写了批次号，可能找到多条记录。如果填写了跟踪号（托盘号），可能找到多条记录
                        if (twMovementDetails.getFmSerialNo() != null) {
                            Assist assist = new Assist();
                            assist.setOrder(Assist.order("inb_serial_no", true));
                            assist.setRequires(Assist.andEq("part_no_version", twMovementDetails.getPartNoVersion()));
                            assist.setRequires(Assist.andEq("loc_no", twMovementDetails.getToLocation()));
                            assist.setRequires(Assist.andEq("inb_serial_no", twMovementDetails.getFmSerialNo()));
                            List<TwInvSkuLoctl> twInvSkuLoctls = twInvSkuLoctlDao.selectTwInvSkuLoctl(assist);
                            int needOutCount = twMovementDetails.getToQty();
                            needOutCount = matchingAndFIFO(twMovementDetails, twInvSkuLoctls, needOutCount);
                            if (needOutCount > 0) {
                                throw new ServiceException("库存不足，分配失败");
                            }
                        } else {
                            Assist assist = new Assist();
                            assist.setRequires(Assist.andEq("part_no_version", twMovementDetails.getPartNoVersion()));
                            assist.setRequires(Assist.andEq("loc_no", twMovementDetails.getToLocation()));
                            assist.setRequires(Assist.andEq("lot_no", twMovementDetails.getFmLotNo()));
                            List<TwInvSkuLoctl> twInvSkuLoctls = twInvSkuLoctlDao.selectTwInvSkuLoctl(assist);
                            int needOutCount = twMovementDetails.getToQty();
                            needOutCount = matchingAndFIFO(twMovementDetails, twInvSkuLoctls, needOutCount);
                            if (needOutCount > 0) {
                                throw new ServiceException("库存不足，分配失败");
                            }
                        }
                    }
                }
                // 【7.7】：更新tw_movement_details
                // 订单行状态 0新单1发布2完成3关闭4分配完成5部分分配'
                twMovementDetails.setLineStatus("4");
                twMovementDetailsDao.updateById(twMovementDetails);

                // 【7.8】：更新tw_movement_header的状态
                TwMovementHeader twMovementHeader1 = new TwMovementHeader();
                twMovementHeader1.setTwMovementHeaderId(twMovementDetails.getTwMovementHeaderId());
                // 移动单状态0新单1发布2完成3关闭4分配完成5部分分配
                twMovementHeader1.setSheetStatus("4");
                twMovementHeaderDao.updateById(twMovementHeader1);
            }
        }
    }

    private int matchingAndFIFO(TwMovementDetails twMovementDetails, List<TwInvSkuLoctl> twInvSkuLoctls,
                                int needOutCount) {
        for (TwInvSkuLoctl twInvSkuLoctl : twInvSkuLoctls) {
            if (needOutCount >= twInvSkuLoctl.getCurQty()) {
                needOutCount -= twInvSkuLoctl.getCurQty();
                // 向tw_act_stock_alloc_s(移动分配表-源地点)新增记录，并且更新库存的记录。源库位的可用库存、待移出数量
                addTwActStockAllocSAndUpdateSku(twMovementDetails, twInvSkuLoctl, twInvSkuLoctl.getCurQty());
                // 向tw_act_stock_alloc_ｄ(移动分配表-目标地点)新增记录，并且更新库存的记录。目标库位的待移入数量
                addtwactstockallocAndupdatesku(twMovementDetails, twInvSkuLoctl, twInvSkuLoctl.getCurQty());
            } else {
                // 向tw_act_stock_alloc_s(移动分配表-源地点)新增记录，并且更新库存的记录。源库位的可用库存、待移出数量
                addTwActStockAllocSAndUpdateSku(twMovementDetails, twInvSkuLoctl, needOutCount);
                // 向tw_act_stock_alloc_ｄ(移动分配表-目标地点)新增记录，并且更新库存的记录。目标库位的待移入数量
                addtwactstockallocAndupdatesku(twMovementDetails, twInvSkuLoctl, needOutCount);
            }
        }
        return needOutCount;
    }

    private int notExcessFIFO(TwMovementDetails twMovementDetails, List<TwInvSkuLoctl> twInvSkuLoctls,
                              int needOutCount) {
        for (TwInvSkuLoctl twInvSkuLoctl : twInvSkuLoctls) {
            if (needOutCount >= twInvSkuLoctl.getCurQty()) {
                needOutCount -= twInvSkuLoctl.getCurQty();
                // 向tw_act_stock_alloc_s(移动分配表-源地点)新增记录，并且更新库存的记录。源库位的可用库存、待移出数量
                addTwActStockAllocSAndUpdateSku(twMovementDetails, twInvSkuLoctl, twInvSkuLoctl.getCurQty());
                // 向tw_act_stock_alloc_ｄ(移动分配表-目标地点)新增记录，并且更新库存的记录。目标库位的待移入数量
                addtwactstockallocAndupdatesku(twMovementDetails, twInvSkuLoctl, twInvSkuLoctl.getCurQty());
            }
        }
        return needOutCount;
    }

    private int excessFIFO(TwMovementDetails twMovementDetails, List<TwInvSkuLoctl> twInvSkuLoctls, int needOutCount) {
        for (TwInvSkuLoctl twInvSkuLoctl : twInvSkuLoctls) {
            if (needOutCount >= twInvSkuLoctl.getCurQty()) {
                needOutCount -= twInvSkuLoctl.getCurQty();
                // 向tw_act_stock_alloc_s(移动分配表-源地点)新增记录，并且更新库存的记录。源库位的可用库存、待移出数量
                addTwActStockAllocSAndUpdateSku(twMovementDetails, twInvSkuLoctl, twInvSkuLoctl.getCurQty());
                // 向tw_act_stock_alloc_ｄ(移动分配表-目标地点)新增记录，并且更新库存的记录。目标库位的待移入数量
                addtwactstockallocAndupdatesku(twMovementDetails, twInvSkuLoctl, twInvSkuLoctl.getCurQty());
            } else {
                // 向tw_act_stock_alloc_s(移动分配表-源地点)新增记录，并且更新库存的记录。源库位的可用库存、待移出数量
                addTwActStockAllocSAndUpdateSku(twMovementDetails, twInvSkuLoctl, twInvSkuLoctl.getCurQty());
                // 向tw_act_stock_alloc_ｄ(移动分配表-目标地点)新增记录，并且更新库存的记录。目标库位的待移入数量
                addtwactstockallocAndupdatesku(twMovementDetails, twInvSkuLoctl, twInvSkuLoctl.getCurQty());
            }
        }
        return needOutCount;
    }

    /**
     * 向tw_act_stock_alloc_s(移动分配表-源地点)新增记录，并且更新库存的记录。源库位的可用库存、待移出数量
     *
     * @param twMovementDetails
     *            a
     * @param twInvSkuLoctl
     *            a
     * @param needOutCount
     *            a
     */
    private void addTwActStockAllocSAndUpdateSku(TwMovementDetails twMovementDetails, TwInvSkuLoctl twInvSkuLoctl,
                                                 int needOutCount) {

        // 【7.1】 插入 移动分配表-源地点 记录
        TwActStockAllocS twActStockAllocS = new TwActStockAllocS();
        // 移库数量-->分配数量
        twActStockAllocS.setAllocQty(twMovementDetails.getToQty());
        twActStockAllocS.setAllocType(twMovementDetails.getSapTransType());
        twActStockAllocS.setPartNoVersion(twMovementDetails.getPartNoVersion());
        twActStockAllocS.setSupplNo(twMovementDetails.getSupplNo());
        twActStockAllocS.setsWarehouseNo(twMovementDetails.getFmWarehouse());
        twActStockAllocS.setsDlocNo(twMovementDetails.getFmDoc());
        twActStockAllocS.setsLocNo(twMovementDetails.getFmLocation());
        twActStockAllocS.setsTmBasStorageId(twInvSkuLoctl.getTmBasStorageId());
        twActStockAllocS.setsLotNo(twInvSkuLoctl.getLotNo());
        twActStockAllocS.setsSerialNo(twInvSkuLoctl.getInbSerialNo());
        twActStockAllocS.setsPartPackSerial(twInvSkuLoctl.getPartPackSerial());
        twActStockAllocS.setsPackNo(twMovementDetails.getInbPackNo());
        twActStockAllocS.setsPackQty(twMovementDetails.getInbPackQty());
        twActStockAllocS.setsPackNum(twMovementDetails.getPackNum());
        twActStockAllocS.setTwMovementHeaderId(twMovementDetails.getTwMovementHeaderId());
        twActStockAllocS.setTwMovementDetailsId(twMovementDetails.getTwMovementDetailsId());
        twActStockAllocS.setMarkStatus(1);
        twActStockAllocS.setCreateTime(new Date());
        twActStockAllocS.setCreateUser(SpringWebUtils.getRequestUser());
        twActStockAllocS.setCompanyCode(SpringWebUtils.getRequestCompany());
        twActStockAllocSMapper.insert(twActStockAllocS);

        // 【7.2】：更新库存(源库位的可用库存、待移出数量)
        TwInvSkuLoctl twInvSkuLoctl1 = new TwInvSkuLoctl();
        twInvSkuLoctl1.setTwInvSkuLoctlId(twInvSkuLoctl.getTwInvSkuLoctlId());
        // 可用库存
        twInvSkuLoctl1.setAvailableQty(twInvSkuLoctl.getAvailableQty() - needOutCount);
        // 待移出数量
        twInvSkuLoctl1.setrOubQty(twMovementDetails.getToQty());
        twInvSkuLoctlDao.updateNonEmptyTwInvSkuLoctlById(twInvSkuLoctl1);
    }

    /**
     * 向tw_act_stock_alloc_ｄ(移动分配表-目标地点)新增记录，并且更新库存的记录。源库位的可用库存、待移出数量
     *
     * @param twMovementDetails
     *            a
     * @param twInvSkuLoctl
     *            d
     * @param needOutCount
     *            ds
     */
    private void addtwactstockallocAndupdatesku(TwMovementDetails twMovementDetails, TwInvSkuLoctl twInvSkuLoctl,
                                                int needOutCount) {

        TwActStockAllocD twActStockAllocD = new TwActStockAllocD();
        twActStockAllocD.setAllocQty(twMovementDetails.getToQty());
        twActStockAllocD.setAllocType(twMovementDetails.getSapTransType());
        twActStockAllocD.setPartNoVersion(twMovementDetails.getPartNoVersion());
        twActStockAllocD.setSupplNo(twMovementDetails.getSupplNo());
        twActStockAllocD.setdWarehouseNo(twMovementDetails.getToWarehouse());
        twActStockAllocD.setdDlocNo(twMovementDetails.getToDoc());
        twActStockAllocD.setdLocNo(twMovementDetails.getToLocation());
        List<TmBasLoc> basLocList = tmBasLocDao.selectTmBasLoc(
                new Assist(Assist.andEq("storage_no", twMovementDetails.getToLocation())));
        twActStockAllocD.setdTmBasStorageId(basLocList.get(0).getTmBasLocId());

        // 更新
        if (twMovementDetails.getToLotNo() != null || twMovementDetails.getToSerailNo() != null
                || twMovementDetails.getToPartPackSerial() != null) {
            TwInvSkuLoctl twInvSkuLoctl1 = new TwInvSkuLoctl();
            twInvSkuLoctl1.setrInbQty(needOutCount);
            Assist assist = new Assist();
            assist.setRequires(Assist.andEq("part_no_version", twMovementDetails.getPartNoVersion()));
            assist.setRequires(Assist.andEq("loc_no", twMovementDetails.getToLocation()));
            if (twMovementDetails.getToLotNo() != null) {
                assist.setRequires(Assist.andEq("lot_no", twMovementDetails.getToLotNo()));
            }
            if (twMovementDetails.getToSerailNo() != null) {
                assist.setRequires(Assist.andEq("inb_serial_no", twMovementDetails.getToSerailNo()));
            }
            if (twMovementDetails.getToPartPackSerial() != null) {
                assist.setRequires(Assist.andEq("part_pack_serial", twMovementDetails.getToPartPackSerial()));
            }
            twInvSkuLoctlDao.updateNonEmptyTwInvSkuLoctl(twInvSkuLoctl1, assist);

            twActStockAllocD.setdLotNo(twMovementDetails.getToLotNo());
            twActStockAllocD.setdSerialNo(twMovementDetails.getToSerailNo());
            twActStockAllocD.setdPartPackSerial(twMovementDetails.getToPartPackSerial());
        } else {
            // 新增
            TwInvSkuLoctl twInvSkuLoctl1 = new TwInvSkuLoctl();
            BeanUtils.copyProperties(twActStockAllocD, twInvSkuLoctl1);
            twInvSkuLoctl1.setTmBasStorageId(basLocList.get(0).getTmBasLocId());
            // 箱数
            twInvSkuLoctl1.setPackageNum(1);
            // 可用数量
            twInvSkuLoctl1.setAvailableQty(0);
            // 库存数量
            twInvSkuLoctl1.setCurQty(0);
            // 待入库数量
            twInvSkuLoctl1.setrInbQty(needOutCount);
            // ASN待入库上架数量-ASN_R_INB_QTY
            twInvSkuLoctl1.setAsnrInbQty(0);
            twInvSkuLoctl1.setrInbQty(twMovementDetails.getToQty());
            twInvSkuLoctl1.setLocNo(twMovementDetails.getToLocation());
            twInvSkuLoctl1.setDlocNo(twMovementDetails.getToDoc());
            twInvSkuLoctl1.setWarehouseNo(twMovementDetails.getToWarehouse());
            twInvSkuLoctl1.setLotNo(twInvSkuLoctl.getLotNo());
            twInvSkuLoctl1.setInbSerialNo(twInvSkuLoctl.getInbSerialNo());
            twInvSkuLoctl1.setPartPackSerial(twInvSkuLoctl.getPartPackSerial());
            twInvSkuLoctl1.setCompanyCode(SpringWebUtils.getRequestCompany());
            twInvSkuLoctl1.setCreateUser(SpringWebUtils.getRequestUser());
            twInvSkuLoctl1.setCreateTime(new Date());
            twInvSkuLoctl1.setMarkStatus(1);
            twInvSkuLoctlDao.insertNonEmptyTwInvSkuLoctl(twInvSkuLoctl1);

            twActStockAllocD.setdLotNo(twInvSkuLoctl.getLotNo());
            twActStockAllocD.setdSerialNo(twInvSkuLoctl.getInbSerialNo());
            twActStockAllocD.setdPartPackSerial(twInvSkuLoctl.getPartPackSerial());
        }
        twActStockAllocD.setdPackNo(twMovementDetails.getInbPackNo());
        twActStockAllocD.setdPackQty(twMovementDetails.getInbPackQty());
        twActStockAllocD.setdPackNum(twMovementDetails.getPackNum());
        twActStockAllocD.setTwMovementHeaderId(twMovementDetails.getTwMovementHeaderId());
        twActStockAllocD.setTwMovementDetailsId(twMovementDetails.getTwMovementDetailsId());
        twActStockAllocD.setMarkStatus(1);
        twActStockAllocD.setCreateTime(new Date());
        twActStockAllocD.setCreateUser(SpringWebUtils.getRequestUser());
        twActStockAllocD.setCompanyCode(SpringWebUtils.getRequestCompany());
        twActStockAllocDMapper.insert(twActStockAllocD);
    }

    /**
     * 设置源库位 并且插入(tw_act_stock_alloc_s)移动分配表源地点记录
     *
     * @param twMovementDetails
     *            a 明细表
     * @param trBasPartStorage
     *            a 零件仓储关系
     */
    private void setFmLocation(TwMovementDetails twMovementDetails, TrBasPartStorage trBasPartStorage) {
        if (trBasPartStorage.getWarehouseNo().equals(twMovementDetails.getFmWarehouse())) {
            if (twMovementDetails.getFmDoc() == null) {
                twMovementDetails.setFmDoc(trBasPartStorage.getDlocNo());
                twMovementDetails.setFmLocation(trBasPartStorage.getLocNo());
            } else {
                if (trBasPartStorage.getDlocNo().equals(twMovementDetails.getFmDoc())) {
                    twMovementDetails.setFmLocation(trBasPartStorage.getLocNo());
                }
            }
            // 根据零件号和分配的库位 查询出库存记录
            List<TwInvSkuLoctl> twInvSkuLoctls = twInvSkuLoctlDao.selectTwInvSkuLoctl(
                    new Assist(Assist.andEq("tm_bas_storage_id", trBasPartStorage.getLocId()),
                            Assist.andEq("part_no_version", twMovementDetails.getPartNoVersion())));
            TwInvSkuLoctl twInvSkuLoctl = twInvSkuLoctls.get(0);
            // 【5.1】 插入 移动分配表-源地点 记录
            TwActStockAllocS twActStockAllocS = new TwActStockAllocS();
            // 移库数量-->分配数量
            twActStockAllocS.setAllocQty(twMovementDetails.getToQty());
            twActStockAllocS.setAllocType(twMovementDetails.getSapTransType());
            twActStockAllocS.setPartNoVersion(twMovementDetails.getPartNoVersion());
            twActStockAllocS.setSupplNo(twMovementDetails.getSupplNo());
            twActStockAllocS.setsWarehouseNo(trBasPartStorage.getWarehouseNo());
            twActStockAllocS.setsDlocNo(trBasPartStorage.getDlocNo());
            twActStockAllocS.setsLocNo(trBasPartStorage.getLocNo());
            twActStockAllocS.setsTmBasStorageId(trBasPartStorage.getLocId());
            twActStockAllocS.setsLotNo(twInvSkuLoctl.getLotNo());
            twActStockAllocS.setsSerialNo(twInvSkuLoctl.getInbSerialNo());
            twActStockAllocS.setsPartPackSerial(twInvSkuLoctl.getPartPackSerial());
            twActStockAllocS.setsPackNo(twMovementDetails.getInbPackNo());
            twActStockAllocS.setsPackQty(twMovementDetails.getInbPackQty());
            twActStockAllocS.setsPackNum(twMovementDetails.getPackNum());
            twActStockAllocS.setTwMovementHeaderId(twMovementDetails.getTwMovementHeaderId());
            twActStockAllocS.setTwMovementDetailsId(twMovementDetails.getTwMovementDetailsId());
            twActStockAllocS.setMarkStatus(1);
            twActStockAllocS.setCreateTime(new Date());
            twActStockAllocS.setCreateUser(SpringWebUtils.getRequestUser());
            twActStockAllocS.setCompanyCode(SpringWebUtils.getRequestCompany());
            twActStockAllocSMapper.insert(twActStockAllocS);

            // 更新库存(源库位的可用库存、待移出数量)
            TwInvSkuLoctl twInvSkuLoctl1 = new TwInvSkuLoctl();
            twInvSkuLoctl1.setTwInvSkuLoctlId(twInvSkuLoctl.getTwInvSkuLoctlId());
            twInvSkuLoctl1.setAvailableQty(twInvSkuLoctl.getAvailableQty() - twMovementDetails.getToQty());
            twInvSkuLoctl1.setrOubQty(twMovementDetails.getToQty());
            twInvSkuLoctlDao.updateNonEmptyTwInvSkuLoctlById(twInvSkuLoctl1);
        }
    }

    /**
     * 设置目标库位 并且插入(tw_act_stock_alloc_d)移动分配表目标地点记录
     *
     * @param twMovementDetails
     *            明细表
     * @param trBasPartStorage
     *            零件仓储关系
     */
    private void setToLocation(TwMovementDetails twMovementDetails, TrBasPartStorage trBasPartStorage) {
        if (trBasPartStorage.getWarehouseNo().equals(twMovementDetails.getToWarehouse())) {
            if (twMovementDetails.getToDoc() == null) {
                twMovementDetails.setToDoc(trBasPartStorage.getDlocNo());
                twMovementDetails.setToLocation(trBasPartStorage.getLocNo());
            } else {
                if (trBasPartStorage.getDlocNo().equals(twMovementDetails.getFmDoc())) {
                    twMovementDetails.setToLocation(trBasPartStorage.getLocNo());
                }
            }
            // 根据零件号和分配的库位 查询出库存记录
            List<TwInvSkuLoctl> twInvSkuLoctls1 = twInvSkuLoctlDao.selectTwInvSkuLoctl(
                    new Assist(Assist.andEq("tm_bas_storage_id", trBasPartStorage.getLocId()),
                            Assist.andEq("part_no_version", twMovementDetails.getPartNoVersion())));
            TwInvSkuLoctl twInvSkuLoctl1 = twInvSkuLoctls1.get(0);
            // 【6.1】：插入 移动分配表-源地点 记录
            TwActStockAllocD twActStockAllocD = new TwActStockAllocD();
            // 移库数量-->分配数量
            twActStockAllocD.setAllocQty(twMovementDetails.getToQty());
            twActStockAllocD.setAllocType(twMovementDetails.getSapTransType());
            twActStockAllocD.setPartNoVersion(twMovementDetails.getPartNoVersion());
            twActStockAllocD.setSupplNo(twMovementDetails.getSupplNo());
            twActStockAllocD.setdWarehouseNo(trBasPartStorage.getWarehouseNo());
            twActStockAllocD.setdDlocNo(trBasPartStorage.getDlocNo());
            twActStockAllocD.setdLocNo(trBasPartStorage.getLocNo());
            twActStockAllocD.setdTmBasStorageId(trBasPartStorage.getLocId());
            twActStockAllocD.setdLotNo(twInvSkuLoctl1.getLotNo());
            twActStockAllocD.setdSerialNo(twInvSkuLoctl1.getInbSerialNo());
            twActStockAllocD.setdPartPackSerial(twInvSkuLoctl1.getPartPackSerial());
            twActStockAllocD.setdPackNo(twMovementDetails.getInbPackNo());
            twActStockAllocD.setdPackQty(twMovementDetails.getInbPackQty());
            twActStockAllocD.setdPackNum(twMovementDetails.getPackNum());
            twActStockAllocD.setTwMovementHeaderId(twMovementDetails.getTwMovementHeaderId());
            twActStockAllocD.setTwMovementDetailsId(twMovementDetails.getTwMovementDetailsId());
            twActStockAllocD.setMarkStatus(1);
            twActStockAllocD.setCreateTime(new Date());
            twActStockAllocD.setCreateUser(SpringWebUtils.getRequestUser());
            twActStockAllocD.setCompanyCode(SpringWebUtils.getRequestCompany());
            twActStockAllocDMapper.insert(twActStockAllocD);

            // 更新库存(源库位的可用库存、待移出数量)
            TwInvSkuLoctl twInvSkuLoctl2 = new TwInvSkuLoctl();
            twInvSkuLoctl2.setTwInvSkuLoctlId(twInvSkuLoctl1.getTwInvSkuLoctlId());
            twInvSkuLoctl2.setAvailableQty(twInvSkuLoctl1.getAvailableQty() - twMovementDetails.getToQty());
            twInvSkuLoctl2.setrOubQty(twMovementDetails.getToQty());
            twInvSkuLoctlDao.updateNonEmptyTwInvSkuLoctlById(twInvSkuLoctl2);
        }
    }

    /**
     * 执行移动库存操作 执行完成后，调整库存(源库位库存转移至目标库位)
     *
     * @param twMovementHeader
     *            头信息
     */
    @Override
    public void execute(TwMovementHeader twMovementHeader) {

        // 【1】：根据移库单的头ID，查询出对应的detail
        Map<String, Object> stringObjectMap = new HashMap<>(40);
        stringObjectMap.put("tw_movement_header_id", twMovementHeader.getTwMovementHeaderId());
        List<TwMovementDetails> listTwMovementDetails = twMovementDetailsDao.selectByMap(stringObjectMap);

        for (TwMovementDetails twMovementDetails : listTwMovementDetails) {
            if (!("4".equals(twMovementDetails.getLineStatus()) || "5".equals(twMovementDetails.getLineStatus()))) {
                throw new ServiceException("必须先分配才能执行");
            }
            if (StringUtils.isBlank(twMovementDetails.getFmLocation())
                    || StringUtils.isBlank(twMovementDetails.getToLocation())) {
                throw new ServiceException(twMovementDetails.getPartNoVersion() + "的源库位或目标库位为空，请先分配");
            }
            // 【2】：修改库存表目标库位的库存
            TwActStockAllocD twActStockAllocD = new TwActStockAllocD();
            twActStockAllocD.setPartNoVersion(twMovementDetails.getPartNoVersion());
            twActStockAllocD.setdLocNo(twMovementDetails.getToLocation());
            twActStockAllocD.setTwMovementDetailsId(twMovementDetails.getTwMovementDetailsId());
            HashMap<String, Object> map = new HashMap<>();
            map.put("part_no_version", twMovementDetails.getPartNoVersion());
            map.put("d_loc_no", twMovementDetails.getToLocation());
            map.put("tw_movement_details_id", twMovementDetails.getTwMovementDetailsId());
            List<TwActStockAllocD> twActStockAllocD1s = twActStockAllocDMapper.selectByMap(map);
            for (TwActStockAllocD twActStockAllocD1 : twActStockAllocD1s) {
                Assist assist1 = new Assist();
                assist1.setRequires(Assist.andEq("loc_no", twMovementDetails.getToLocation()));
                assist1.setRequires(Assist.andEq("part_no_version", twMovementDetails.getPartNoVersion()));
                assist1.setRequires(Assist.andEq("part_pack_serial", twActStockAllocD1.getdPartPackSerial()));
                List<TwInvSkuLoctl> twInvSkuLoctls = twInvSkuLoctlDao.selectTwInvSkuLoctl(assist1);

                TwInvSkuLoctl twInvSkuLoctl2 = new TwInvSkuLoctl();
                twInvSkuLoctl2.setTwInvSkuLoctlId(twInvSkuLoctls.get(0).getTwInvSkuLoctlId());
                twInvSkuLoctl2.setCurQty(twInvSkuLoctls.get(0).getCurQty() + twMovementDetails.getToQty());
                twInvSkuLoctl2.setAvailableQty(twInvSkuLoctls.get(0).getAvailableQty() + twMovementDetails.getToQty());
                twInvSkuLoctl2.setrInbQty(0);
                twInvSkuLoctlDao.updateNonEmptyTwInvSkuLoctlById(twInvSkuLoctl2);
            }

            // 【6】：修改库存表源库位的库存
            TwActStockAllocS twActStockAllocS = new TwActStockAllocS();
            twActStockAllocS.setPartNoVersion(twMovementDetails.getPartNoVersion());
            twActStockAllocS.setsLocNo(twMovementDetails.getToLocation());
            twActStockAllocS.setTwMovementDetailsId(twMovementDetails.getTwMovementDetailsId());

            HashMap<String, Object> map2 = new HashMap<>();
            map2.put("part_no_version", twMovementDetails.getPartNoVersion());
            map2.put("s_loc_no", twMovementDetails.getFmLocation());
            map2.put("tw_movement_details_id", twMovementDetails.getTwMovementDetailsId());
            List<TwActStockAllocS> twActStockAllocS1 = twActStockAllocSMapper.selectByMap(map2);
            for (TwActStockAllocS twActStockAllocS2 : twActStockAllocS1) {
                Assist assist2 = new Assist();
                assist2.setRequires(Assist.andEq("loc_no", twMovementDetails.getFmLocation()));
                assist2.setRequires(Assist.andEq("part_no_version", twMovementDetails.getPartNoVersion()));
                assist2.setRequires(Assist.andEq("part_pack_serial", twActStockAllocS2.getsPartPackSerial()));
                List<TwInvSkuLoctl> twInvSkuLoctls1 = twInvSkuLoctlDao.selectTwInvSkuLoctl(assist2);

                TwInvSkuLoctl twInvSkuLoctl3 = new TwInvSkuLoctl();
                twInvSkuLoctl3.setTwInvSkuLoctlId(twInvSkuLoctls1.get(0).getTwInvSkuLoctlId());
                twInvSkuLoctl3.setCurQty(twInvSkuLoctls1.get(0).getCurQty() - twMovementDetails.getToQty());
                twInvSkuLoctl3.setrOubQty(0);
                twInvSkuLoctlDao.updateNonEmptyTwInvSkuLoctlById(twInvSkuLoctl3);
            }
        }
    }
}
